<?php
namespace com;
/**
* 通用的树型类，可以生成任何树型结构  ‎2018‎年‎2‎月‎22‎日，‏‎3:54:19 
* 
* $tree = new \com\Tree( $list  ,'id' ,'pid' ,  'name' , '0' );
* $tree-> XXX...
*/
class tree {
    /**
    * 生成树型结构所需要的2维数组
    * @var array
    */
    private $arr = array();

    /**
    * 生成树型结构所需修饰符号，可以换成图片
    * @var array
    */
    public $icon = array('│','├','└');
    
    public $nbsp = "&nbsp;&nbsp;&nbsp;&nbsp;";  // 4空格

    public $id = 'id';			//分类id 
    public $pid = 'pid';		//父pid
	public $name = 'name';      //标题文字
	public $root = '0';			//跟 pid 

	


/***************** 以上都是 类的 变量 ************************/

    /**
     *  PHP5版本 构造函数  需要写在php4 构造函数  前 
     *
     * @access    private
     * @param     string  $tplfile  模板文件
     * @return    void
     */
    function __construct( $arr=array() , $id = 'id' , $pid = 'pid' ,  $name = 'name' , $root = '0' , $nbsp = "&nbsp;&nbsp;&nbsp;&nbsp;" , $icon = array('│','├','└')  )
    {

        return $this->init( $arr , $id , $pid ,  $name, $root , $nbsp , $icon  ); //调用初始化函数

    }
    /**
     *  兼容PHP4版本  构造函数与 类名相同
     *
     * @access    private
     * @param     string  $tplfile  模板文件
     * @return    void
     */
    function tree( $arr=array() , $id = 'id' , $pid = 'pid' ,  $name = 'name' , $root = '0' , $nbsp = "&nbsp;&nbsp;&nbsp;&nbsp;" , $icon = array('│','├','└')  )
    {
        return $this->__construct( $arr , $id , $pid ,  $name, $root , $nbsp , $icon );

    }

    /**
    * 构造函数，初始化类
    * @param array 2维数组，例如：
    * array(
    *      1 => array('id'=>'1','pid'=>0,'name'=>'一级栏目一'),
    *      2 => array('id'=>'2','pid'=>0,'name'=>'一级栏目二'),
    *      3 => array('id'=>'3','pid'=>1,'name'=>'二级栏目一'),
    *      4 => array('id'=>'4','pid'=>1,'name'=>'二级栏目二'),
    *      5 => array('id'=>'5','pid'=>2,'name'=>'二级栏目三'),
    *      6 => array('id'=>'6','pid'=>3,'name'=>'三级栏目一'),
    *      7 => array('id'=>'7','pid'=>3,'name'=>'三级栏目二')
    *      )
    */

    public function init(   $arr=array() , $id = 'id' , $pid = 'pid' ,  $name = 'name' , $root = '0' , $nbsp = "&nbsp;&nbsp;&nbsp;&nbsp;" , $icon = array('│','├','└')    ){
	
		$this->set(  $id , $pid ,  $name, $root , $nbsp , $icon  );

       if( !is_array($arr) )  return  false ;

       //将 id 转换 key 
       $arr2 = array();
       foreach( $arr as $k => $v ){
            $arr2[$v[$this->id]] = $v; 
       }

       $this->arr = $arr2;
       return $this;
    }

	public function set(  $id = 'id' , $pid = 'pid' ,  $name = 'name' , $root = '0' , $nbsp = "&nbsp;&nbsp;&nbsp;&nbsp;" , $icon = array('│','├','└')  ){
		
		$this->id = $id;
		$this->pid = $pid;
		$this->name = $name;
		$this->root = $root;
		$this->nbsp = $nbsp;
		$this->icon = $icon;

		return $this;
	}

    public function setid($id){

       $this->id = $id;
       return $this;

    }
    public function setpid($pid){

       $this->pid = $pid;
       return $this;

    }
	public function setname($name){

       $this->name = $name;
       return $this;

    }
	public function setroot($root){

       $this->root = $root;
       return $this;

    }

	public function seticon($icon){

       $this->icon = $icon;
       return $this;

    }

	public function setnbsp($nbsp){

       $this->nbsp = $nbsp;
       return $this;

    }


    /**
    * 得到父栏目 ( 爸爸是谁？一维数组)
    * @param int
    * @return array
    */
    public function  getP($myid){
        $pid = isset( $this->arr[$myid][$this->pid] ) ? $this->arr[$myid][$this->pid] : null ; 
        return isset( $this->arr[$pid] ) ? $this->arr[$pid] : null ; 
    }
    /**
    * 得到父级数组 （爸爸 叔叔 大爷  同一个爷爷的   二维数组 ）
    * @param int
    * @return array
    */
    public function getPP($myid){
        $newarr = array();
        if(!isset($this->arr[$myid])) return false;
        $pid = $this->arr[$myid][$this->pid];

        if(!isset($this->arr[$pid])) return false;
        $pid = $this->arr[$pid][$this->pid];
        
        if(is_array($this->arr)){
            foreach($this->arr as $id => $a){
                if($a[$this->pid] == $pid) $newarr[$id] = $a;
            }
        }
        return $newarr;
    }


    /**
    * 得到当前位置数组   （   二维数组）
    * @param int
    * @return array
    */
	public function getPos($myid){
        return $this->get_pos($myid);
    }
    public function get_pos($myid,&$newarr = array()){
        $a = array();
        if(!isset($this->arr[$myid])) return $a;
        $newarr[] = $this->arr[$myid];
        $pid = $this->arr[$myid][$this->pid];
        if(isset($this->arr[$pid])){
            $this->get_pos($pid,$newarr);
        }
        if(is_array($newarr)){
            krsort($newarr);
            foreach($newarr as $v){
                $a[$v[$this->id]] = $v;
            }
        }
        return $a;
    }




    //当前位置HTML：（ 当前位置 html结构 name  url  需要修改  不建议放在 tree 中 ）
    public function getPlace($myid){
        $arr =  $this->get_pos($myid);
        $html = '<a href="/" >首页</a> &gt; ';
        foreach($arr as $k => $v ){
            $html .= '<a href="/type.php?id='.$v[$this->id].'" >'.$v['typename'].'</a> &gt; ';
        }
        return $html;
    }




    /**
    * 得到子级数组 （ 儿子1 儿子2 儿子3...   同一个父亲下的   二维数组 ）
    * @param int
    * @return array
    */
	public function getC($myid){
        return $this->get_child($myid);
    }
    public function get_child($myid){
        $a = $newarr = array();
        if(is_array($this->arr)){
            foreach($this->arr as $id => $a){
                if($a[$this->pid] == $myid) $newarr[$id] = $a;
            }
        }
        return $newarr ? $newarr : array();
    }



    /**
     * 获取全部子孙栏目   （ 指保留 id   全部 子孙id   一位数组 ）
     * Enter description here ...
     * @param unknown_type $myid
     */
	public function getCC($myid, $is_only_id = true ){
        return $this->get_child_all($myid ,  $is_only_id );
    }
    public function get_child_all ($myid=0, $is_only_id = false , &$newarr = array()) {
		foreach($this->arr as $id => $a ){
            if( $a[$this->pid] == $myid ){
                //开始递归
                $this->get_child_all( $id , $is_only_id  ,  $newarr);

				if(  $is_only_id ){
					$newarr[$id] =  $id;// 等于id
				}else{
					$newarr[$id] = $a;//等于row
				}
                
            }
        }
        return $newarr;
    }

    /**
     * 获取全部子孙栏目 id   方便 sql语句  select * from xxx where  id  in( ...ids...);  以及  树形结构 修改 pid  不可以在 ids中 
     * Enter description here ...
     * @param unknown_type $myid
     */
    public function getids($myid , $no_myid = false ){
        $arr =  $this->get_child_all($myid , true );
        if( !$no_myid ){ $arr[$myid] = $myid ; }
        $ids = implode( ',' , $arr );
        return $ids;
    }

    /**
     * 获取 同级self 栏目数组;
     * Enter description here ...
     * @param unknown_type $myid
     */
    public function getS($myid , &$newarr = array() ){
        $pid =  $this->arr[$myid][$this->pid];

        foreach( $this->arr as $k => $v  ){
            if($v[$this->pid] == $pid){
                $newarr[$k] = $v; 
            }
        }
        return $newarr;
    }
    /**
     * 获取 当前栏目;
     * Enter description here ...
     * @param unknown_type $myid
     */
    public function get($myid){
        return isset ($this->arr[$myid] ) ? $this->arr[$myid] : null ;
    }







    //==================================================================



    //获取子孙分类 list
    public function  getCL(){
        $list = array();
        foreach($this->arr as $k => $v){
            $children = $this->getC($v[$this->id]);
            if($children){
                $list[$v[$this->id]] =  $children;
            }
        }
        return $list;
    }
    //获取父亲分类 list
    public function  getPL(){
        $list = array();
        foreach($this->arr as $k => $v){
            $p = $this->getPos($v[$this->id]);
            unset($p[$v[$this->id]]);
            $p = array_reverse($p, true);

            if($p){
                $list[$v[$this->id]] =  $p;
            }
        }
        return $list;

    }
    //获取每个父分类下的子分类
    public function getJs(){
        $res = array();
        foreach($this->arr as $v){
            // $res[$v[$this->pid]][$v[$this->id]] = $v; // ????  在想想。。。
			$res[$v[$this->pid]][$v[$this->id]] = $v[$this->name];
        }
        return $res;
    }

    //==================================================================

    /**
    * 得到树型结构 数组list
    * @param int ID，表示获得这个ID下的所有子级
    * @param int 被选中的ID，比如在做树型下拉框的时候需要用到
    * @return 2维数组
    */
    public function get_tree_list($myid=null,  $sid = 0,     $adds = '', &$ret = array(),$deep = 0  ){
	
		if( $myid === null ){ $myid = $this->root ; }

        $deep++;//深度
        $number=1;
        $child = $this->get_child($myid);



        if(is_array($child)){
            $total = count($child);
            foreach($child as $id=>$value){
                $j=$k='';
                if($number==$total){
                    $j .= $this->icon[2]; // array('│','├','└');
                }else{
                    $j .= $this->icon[1];// array('│','├','└');
                    $k = $adds ? $this->icon[0] : '';// array('│','├','└');
                }
                $value['_deep'] = $deep;
                $value['_prefix'] = $adds ? $adds.$j : '';
                $value['_selected']  = $id==$sid ? 'selected' : '';

                $ret[] = $value;

                $nbsp = $this->nbsp;
                $this->get_tree_list($id,  $sid, $adds.$k.$nbsp  , $ret ,$deep );
                $number++;
            }
        }


        return $ret;
    }

    //==================================================================
	//==================================================================
	//==================================================================
  
    //获取树  这个方法先不要用 没有get_tree_list 好 ====
    //public function getTree($rs , $fid=0){
    public function getTree( $pid= null){  

		if( $pid === null ){ $pid = $this->root ; }

        $rs = $this->arr ;
        $res = array();
        foreach($rs as $v){
            $res[$v[$this->pid]][] = $v;
        }
        return $this->createTree($res,$pid);

    }
    //创建分类树 需要外部调用 getTree
    private function createTree($data,$pid=0,$prefix=NULL){

        if(!$data){
            return;
        }
        if($pid==0){
            $prefix = 0;
        }else{
            $prefix++;
        }
        
        static $tree = array();
        if(array_key_exists($pid,$data)&&!empty($data[$pid])){
            foreach($data[$pid] as $v){
                $v['prefix'] = $prefix;
                if(array_key_exists($v[$this->id],$data)&&!empty($data[$v[$this->id]])){
                    $tree[] = $v;
                    $this->createTree($data,$v[$this->id],$prefix);  
                }else{
                    $tree[] = $v;
                }           
            }
            return $tree;
        }
    }

    //==================================================================

    

    /**
    * 得到树型结构
    * @param int ID，表示获得这个ID下的所有子级
    * @param string 生成树型结构的基本代码，例如："<option value=\$id \$selected>\$spacer\$name</option>"
    * @param int 被选中的ID，比如在做树型下拉框的时候需要用到
    * @return string

	    $tree = new \com\Tree();
        $rs = $tree ->setid('dept_id')->setpid('fid') -> init( $list );
        $treelist =  $tree->get_tree( -9  , "<option value=\$id \$selected>\$spacer\$name</option>") ;
        echo '<select>' . $treelist . '</select>';
    */
    public function get_tree($myid = null, $str, $sid = 0, $adds = '', $str_group = '' , &$ret = ''){

		if( $myid === null ){ $myid = $this->root ; }

        $number=1;
        $child = $this->get_child($myid);



        if(is_array($child)){
            $total = count($child);
            foreach($child as $id=>$value){
                $j=$k='';
                if($number==$total){
                    $j .= $this->icon[2];
                }else{
                    $j .= $this->icon[1];
                    $k = $adds ? $this->icon[0] : '';
                }
                $spacer = $adds ? $adds.$j : '';
                $selected = $id==$sid ? 'selected' : '';
                #print_r($value);echo '<hr/>';
                @extract($value);

                
                $value[$this->pid] == 0 && $str_group ? eval("\$nstr = \"$str_group\";") : eval("\$nstr = \"$str\";");
                $ret .= $nstr;
                $nbsp = $this->nbsp;
                $this->get_tree($id, $str, $sid, $adds.$k.$nbsp,$str_group ,  $ret );
                $number++;
            }
        }


        return $ret;
    }



	//==================================================================

    /**
    * 同上一方法类似,但允许多选
    * $str = "<option value=\$id \$selected>\$spacer \$name</option>".PHP_EOL; //生成的形式
    * $tree->ret='';# ret 是 递归调用时 用到的变量 上边 tree 之后，ret有值 
    * $options = $tree->get_tree_multi(0,$str, $select_id = '1,2,3');
	* echo '<select multiple="multiple">' . $options . '</select>';
    */
    public function get_tree_multi($myid = null, $str, $sid = 0, $adds = '' , &$ret = '' ){

		if( $myid === null ){ $myid = $this->root ; }

        $number=1;
        $child = $this->get_child($myid);

		dump($child);

        if(is_array($child)){
            $total = count($child);
            foreach($child as $id=>$a){
                $j=$k='';
                if($number==$total){
                    $j .= $this->icon[2];
                }else{
                    $j .= $this->icon[1];
                    $k = $adds ? $this->icon[0] : '';
                }
                $spacer = $adds ? $adds.$j : '';
                
                $selected = $this->have($sid,$id) ? 'selected' : '';
                
                @extract($a);
                eval("\$nstr = \"$str\";");
                $ret .= $nstr;
                $this->get_tree_multi($id, $str, $sid, $adds.$k.'&nbsp;' , $ret);
                $number++;
            }
        }
        return $ret;
    }
	//==================================================================
     /**
    * @param integer $myid 要查询的ID
    * @param string $str   第一种HTML代码方式 
    * @param string $str2  第二种HTML代码方式
    * @param integer $sid  默认选中
    * @param integer $adds 前缀
    */
    public function get_tree_category($myid= null , $str="<div class=''>\$spacer \$name</div>", $str2="<div class='selected'>\$spacer \$name</div>", $sid = 0, $adds = '' , &$ret = '' ){

		if( $myid === null ){ $myid = $this->root ; }

        $number=1;
        $child = $this->get_child($myid);
        if(is_array($child)){
            $total = count($child);
            foreach($child as $id=>$a){
                $j=$k='';
                if($number==$total){
                    $j .= $this->icon[2];
                }else{
                    $j .= $this->icon[1];
                    $k = $adds ? $this->icon[0] : '';
                }
                $spacer = $adds ? $adds.$j : '';
                
                $selected = $this->have($sid,$id) ? 'selected' : '';

                @extract($a);
                if (empty($selected)) {
                    eval("\$nstr = \"$str\";");
                } else {
                    eval("\$nstr = \"$str2\";");
                }
                $ret .= $nstr;
                $this->get_tree_category($id, $str, $str2, $sid, $adds.$k.'&nbsp;' , $ret);
                $number++;
            }
        }
        return $ret;
    }
    //==================================================================




    /**
     * 同上一类方法，jquery treeview 风格，可伸缩样式（需要treeview插件支持）
     * @param $myid 表示获得这个ID下的所有子级
     * @param $effected_id 需要生成treeview目录数的id
     * @param $str 末级样式
     * @param $str2 目录级别样式
     * @param $showlevel 直接显示层级数，其余为异步显示，0为全部限制
     * @param $style 目录样式 默认 filetree 可增加其他样式如'filetree treeview-famfamfam'
     * @param $currentlevel 计算当前层级，递归使用 适用改函数时不需要用该参数
     * @param $recursion 递归使用 外部调用时为FALSE
     */
    function get_treeview($myid= null ,$effected_id='example',$str="<span class='file'>\$name</span>", $str2="<span class='folder'>\$name</span>" ,$showlevel = 0 ,$style='filetree ' , $currentlevel = 1,$recursion=FALSE  , &$ret = '') {
        
		if( $myid === null ){ $myid = $this->root ; }

		$child = $this->get_child($myid);
        if(!defined('EFFECTED_INIT')){
           $effected = ' id="'.$effected_id.'"';
           define('EFFECTED_INIT', 1);
        } else {
           $effected = '';
        }
        $placeholder =  '<ul><li><span class="placeholder"></span></li></ul>';
        if(!$recursion) $ret .='<ul'.$effected.'  class="'.$style.'">';
        foreach($child as $id=>$a) {

            @extract($a);
            if($showlevel > 0 && $showlevel == $currentlevel && $this->get_child($id)) $folder = 'hasChildren'; //如设置显示层级模式@2011.07.01
            $floder_status = isset($folder) ? ' class="'.$folder.'"' : '';      
            $ret .= $recursion ? '<ul><li'.$floder_status.' id=\''.$id.'\'>' : '<li'.$floder_status.' id=\''.$id.'\'>';
            $recursion = FALSE;
            if($this->get_child($id)){
                eval("\$nstr = \"$str2\";");
                $ret .= $nstr;
                if($showlevel == 0 || ($showlevel > 0 && $showlevel > $currentlevel)) {
                    $this->get_treeview($id, $effected_id, $str, $str2, $showlevel, $style, $currentlevel+1, TRUE , $ret);
                } elseif($showlevel > 0 && $showlevel == $currentlevel) {
                    $ret .= $placeholder;
                }
            } else {
                eval("\$nstr = \"$str\";");
                $ret .= $nstr;
            }
            $ret .=$recursion ? '</li></ul>': '</li>';
        }
        if(!$recursion)  $ret .='</ul>';
        return $ret;
    }
	//==================================================================
    /**
     * 获取子栏目json
     * Enter description here ...
     * @param unknown_type $myid
     */
    public function creat_sub_json($myid=null, $str='') {

		if( $myid === null ){ $myid = $this->root ; }

        $sub_cats = $this->get_child($myid);
        $n = 0;
        if(is_array($sub_cats)) foreach($sub_cats as $c) {  

#print_r($c);

            # $data[$n][$this->id] = @iconv(CHARSET,'utf-8',$c[$this->id]);
            $data[$n][$this->id] = $c[$this->id];

#var_dump(  $this->get_child($c[$this->id])   );
            if($this->get_child($c[$this->id])) {
                $data[$n]['liclass'] = 'hasChildren';
                $data[$n]['children'] = array(array('text'=>'&nbsp;','classes'=>'placeholder'));
                $data[$n]['classes'] = 'folder';
                # $data[$n]['text'] = @iconv(CHARSET,'utf-8',$c['catname']);
                $data[$n]['text'] = $c[$this->name];
            } else {                
                if($str) {
                    @extract(array_iconv($c,CHARSET,'utf-8'));
                    eval("\$data[$n]['text'] = \"$str\";");
                } else {
                    # $data[$n]['text'] = @iconv(CHARSET,'utf-8',$c['catname']);
                    $data[$n]['text'] = $c[$this->name];
                }
            }
            $n++;

#echo '<hr/>';
        }
        return json_encode($data);      
    }
	//==================================================================
    private function have($list,$item){
        return(strpos(',,'.$list.','  ,  ','.$item.','));
    }
    

    
    
};
